import {vec2, vec3} from "gl-matrix";
import {VertexData} from "../core/vertexData";

class IdxGroup {
    static NO_VALUE = -1;
    idxPos = IdxGroup.NO_VALUE;
    idxTexCoord = IdxGroup.NO_VALUE;
    idxNormal = IdxGroup.NO_VALUE;
}

class ObjFace {
    idxGroups: Array<IdxGroup> = [new IdxGroup(), new IdxGroup(), new IdxGroup()];

    constructor(v1: string, v2: string, v3: string) {
        this.parseLine(v1, this.idxGroups[0]);
        this.parseLine(v2, this.idxGroups[1]);
        this.parseLine(v3, this.idxGroups[2]);
    }

    parseLine(v: string, idxGroup: IdxGroup) {
        const tokens = v.split("/");
        const len = tokens.length;
        idxGroup.idxPos = parseInt(tokens[0]) - 1;
        if (len > 1) {
            const texCoord = tokens[1];
            idxGroup.idxTexCoord = texCoord.length > 0 ? parseInt(texCoord) - 1 : IdxGroup.NO_VALUE;
            if (len > 2) {
                idxGroup.idxNormal = tokens[2].length > 0 ? parseInt(tokens[2]) - 1 : IdxGroup.NO_VALUE;
            }
        }
    }
}

export class ObjParser {

    static  tmpVec3_1 = vec3.create();
    static  tmpVec3_2 = vec3.create();

    static loadMesh(objdata: string):VertexData {
        let lines = objdata.split("\n");

        let vertices: Array<vec3> = [];
        let texCoords: Array<vec2> = [];
        let normals: Array<vec3> = [];
        let faces: Array<ObjFace> = [];

        for (let i = 0, ilen = lines.length; i < ilen; ++i) {
            let tokens = lines[i].split(/\s+/g);
            switch (tokens[0]) {
                case 'v':
                    vertices.push(vec3.fromValues(
                        parseFloat(tokens[1]),
                        parseFloat(tokens[2]),
                        parseFloat(tokens[3])
                    ));
                    break;
                case 'vt':
                    texCoords.push(vec2.fromValues(
                        parseFloat(tokens[1]),
                        parseFloat(tokens[2])
                    ));
                    break;
                case 'vn':
                    normals.push(vec3.fromValues(
                        parseFloat(tokens[1]),
                        parseFloat(tokens[2]),
                        parseFloat(tokens[3])
                    ));
                    break;
                case 'f':
                    faces.push(new ObjFace(tokens[1], tokens[2], tokens[3]));
                    break;
                default:
                    // ignore
                    break;
            }
        }

        return ObjParser.reorderList(vertices, texCoords, normals, faces);
    }

    static reorderList(vertices: Array<vec3>, texCoords: Array<vec2>, normals: Array<vec3>, faces: Array<ObjFace>) :VertexData{
        let indicesArray: Array<number> = [];
        let posArray: Array<number> = [];
        for (let i = 0, ilen = vertices.length; i < ilen; ++i) {
            posArray.push(vertices[i][0], vertices[i][1], vertices[i][2])
        }
        let texCoordArray: Array<number> = new Array(vertices.length * 2);
        let normalArray: Array<number> = new Array(vertices.length * 3);
        for (let i = 0, ilen = faces.length; i < ilen; ++i) {
            let idxGroups = faces[i].idxGroups;
            ObjParser.processFaceVertex(idxGroups[0], texCoords, texCoordArray, normals, normalArray, indicesArray);
            ObjParser.processFaceVertex(idxGroups[1], texCoords, texCoordArray, normals, normalArray, indicesArray);
            ObjParser.processFaceVertex(idxGroups[2], texCoords, texCoordArray, normals, normalArray, indicesArray);

            // 如果没用法线数据,手动计算面法线
            if (
                idxGroups[0].idxNormal < 0 ||
                idxGroups[1].idxNormal < 0 ||
                idxGroups[2].idxNormal < 0
            ) {
                let v0 = vertices[idxGroups[0].idxPos];
                let v1 = vertices[idxGroups[1].idxPos];
                let v2 = vertices[idxGroups[2].idxPos];
                let v01 = vec3.sub(ObjParser.tmpVec3_1,v1,v0);
                let v12 = vec3.sub(ObjParser.tmpVec3_2,v2,v1);
                vec3.cross(ObjParser.tmpVec3_1,v01,v12);
                vec3.normalize(ObjParser.tmpVec3_1,ObjParser.tmpVec3_1);
                normalArray[3 * idxGroups[0].idxPos] = ObjParser.tmpVec3_1[0];
                normalArray[3 * idxGroups[0].idxPos + 1] = ObjParser.tmpVec3_1[1];
                normalArray[3 * idxGroups[0].idxPos + 2] = ObjParser.tmpVec3_1[2];

                normalArray[3 * idxGroups[1].idxPos] = ObjParser.tmpVec3_1[0];
                normalArray[3 * idxGroups[1].idxPos + 1] = ObjParser.tmpVec3_1[1];
                normalArray[3 * idxGroups[1].idxPos + 2] = ObjParser.tmpVec3_1[2];

                normalArray[3 * idxGroups[2].idxPos] = ObjParser.tmpVec3_1[0];
                normalArray[3 * idxGroups[2].idxPos + 1] = ObjParser.tmpVec3_1[1];
                normalArray[3 * idxGroups[2].idxPos + 2] = ObjParser.tmpVec3_1[2];
            }
        }
        // console.log("vertices:",posArray);
        // console.log("normals:",normalArray);
        // console.log("texCoords:",texCoordArray);
        return new VertexData({
            positions:posArray,
            normals:normalArray,
            texCoords:texCoordArray,
            indices:indicesArray,
        })
    }

    private static processFaceVertex(idxGroup: IdxGroup, texCoords: Array<vec2>, texCoordArray: number[], normals: Array<vec3>, normalArray: number[], indicesArray: number[]) {
        let posIndex = idxGroup.idxPos;
        indicesArray.push(posIndex);

        // 如果有纹理
        if (idxGroup.idxTexCoord >= 0) {
            texCoordArray[2 * posIndex] = texCoords[idxGroup.idxTexCoord][0];
            texCoordArray[2 * posIndex + 1] = 1 - texCoords[idxGroup.idxTexCoord][1];  // 翻转Y坐标
        } else {
            texCoordArray[2 * posIndex] = 0;
            texCoordArray[2 * posIndex + 1] = 0;
        }

        if (idxGroup.idxNormal >= 0) {
            normalArray[3 * posIndex] = normals[idxGroup.idxNormal][0];
            normalArray[3 * posIndex + 1] = normals[idxGroup.idxNormal][1];
            normalArray[3 * posIndex + 2] = normals[idxGroup.idxNormal][2];
        }
    }
}
